package cn.com.cym.gamezao.service.authorize.impl;

import cn.com.cym.gamezao.base.BaseImpl;
import cn.com.cym.gamezao.base.BaseRepository;
import cn.com.cym.gamezao.base.ResultResponse;
import cn.com.cym.gamezao.base.SearchPage;
import cn.com.cym.gamezao.dto.UserDto;
import cn.com.cym.gamezao.dto.UserPasswordDto;
import cn.com.cym.gamezao.dto.UserResponse;
import cn.com.cym.gamezao.entity.authorize.RoleAuthorize;
import cn.com.cym.gamezao.entity.authorize.User;
import cn.com.cym.gamezao.entity.authorize.UserInfo;
import cn.com.cym.gamezao.entity.authorize.UserRole;
import cn.com.cym.gamezao.enums.UserType;
import cn.com.cym.gamezao.exception.BaseException;
import cn.com.cym.gamezao.jwt.JwtUtils;
import cn.com.cym.gamezao.repository.authorize.UserRepository;
import cn.com.cym.gamezao.service.authorize.UserInfoService;
import cn.com.cym.gamezao.service.authorize.UserService;
import cn.com.cym.gamezao.util.AESUtils;
import cn.com.cym.gamezao.util.CommonUtils;
import cn.com.cym.gamezao.util.MathUtils;
import cn.com.cym.gamezao.util.RSAUtils;
import cn.hutool.core.util.ReflectUtil;
import lombok.SneakyThrows;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;

@Service
@Transactional(rollbackFor = Exception.class)
public class UserImpl extends BaseImpl<User> implements UserService {

    final UserRepository thisRepository;

    final UserInfoService userInfoService;

    final HttpServletRequest request;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    public UserImpl(UserRepository userRepository,UserInfoService userInfoService,HttpServletRequest request){
        this.thisRepository=userRepository;
        this.userInfoService=userInfoService;
        this.request=request;
    }

    @Override
    public BaseRepository<User, Long> getJpaRepository() {
        return this.thisRepository;
    }

    @Override
    public Page<User> findByPageable(SearchPage searchPage) {

        String[] sortBy = searchPage.getSortBy().toArray(new String[searchPage.getSortBy().size()]);

        Pageable pageable = PageRequest.of(searchPage.getCurrent()-1, searchPage.getPageSize(), searchPage.getSort(), sortBy);

        Specification<User> specification = new Specification<>() {

            private static final long serialVersionUID = -2661077778894635593L;

            /**
             * @param root: 代表查询的实体类.
             * @param query: 可以从中可到 Root 对象, 即告知 JPA Criteria 查询要查询哪一个实体类. 还可以
             * 来添加查询条件, 还可以结合 EntityManager 对象得到最终查询的 TypedQuery 对象.
             * @param  cb: CriteriaBuilder 对象. 用于创建 Criteria 相关对象的工厂. 当然可以从中获取到 Predicate 对象
             * @return  Predicate 类型, 代表一个查询条件.
             */
            @SneakyThrows
            @Override
            public Predicate toPredicate(Root<User> root,
                                         CriteriaQuery<?> query, CriteriaBuilder cb) {
                final Map<String, Field> fieldMap = ReflectUtil.getFieldMap(User.class);
                return cb.and(CommonUtils.pageSet(fieldMap,searchPage,cb,root));
            }
        };
        return this.thisRepository.findAll(specification, pageable);
    }

    @Override
    public UserResponse login(UserDto userDto) {

        //TODO 判断验证码是否一致或者失效
        final String code = userDto.getCode();

        RedisTemplate<String, Object> redisTemplate = this.redisTemplate;
        String customIp = CommonUtils.customIp(request);
        Object o = redisTemplate.opsForValue().get(customIp+"LOGIN");
        if(o==null){
            throw  new BaseException("验证码已失效，请重新刷新获取验证码",new ResultResponse("验证码已失效，请重新刷新获取验证码",HttpStatus.BAD_REQUEST.value()));
        }else if(!code.equals(o.toString())){
            throw new BaseException("请输入正确的验证码",new ResultResponse("请输入正确的验证码",HttpStatus.BAD_REQUEST.value()));
        }

        List<User> users = this.thisRepository.fetchByUserName(userDto.getUserName());
        //判断数据是否存在或者密码是否一致

        if(users.size()!=1 || !users.get(0).getPassword().equals(AESUtils.encrypt(userDto.getPassword()))){
            throw new BaseException("用户名或者密码错误",new ResultResponse("用户名或者密码错误",HttpStatus.BAD_REQUEST.value()));
        }

        // 如果用户类型不为空 ，直接抛弃不让使用
        if(CommonUtils.isNotEmpty(users.get(0).getUserType())){
            throw new BaseException("该用户无法登录该平台",new ResultResponse("该用户无法登录该平台",HttpStatus.BAD_REQUEST.value()));
        }

        final String token = JwtUtils.create(users.get(0));
        final User user = users.get(0);
        //获取用户的角色
        final Iterator<UserRole> iterator = user.getUserRoles().iterator();

        Set<String> rules=new HashSet<>();

        if(iterator.hasNext()){
            final Iterator<RoleAuthorize> iteratorRoleAuthorize = iterator.next().getRole().getRoleAuthorizes().iterator();

            if(iteratorRoleAuthorize.hasNext()){
                final RoleAuthorize next = iteratorRoleAuthorize.next();
                rules.add(next.getAuthorize().getCode());
            }

        }

        return new UserResponse(token, RSAUtils.encrypt(userDto.getUserName(), RSAUtils.PUBLIC_KEY),"用户登录成功",rules,user.getUserType());
    }

    @Override
    public UserResponse login(UserDto userDto, Boolean isApplicant) {


        //TODO 判断验证码是否一致或者失效
        final String code = userDto.getCode();

        RedisTemplate<String, Object> redisTemplate = this.redisTemplate;
        String customIp = CommonUtils.customIp(request);
        Object o = redisTemplate.opsForValue().get(customIp+"LOGIN");
        if(o==null){
            throw  new BaseException("验证码已失效，请重新刷新获取验证码",new ResultResponse("验证码已失效，请重新刷新获取验证码",HttpStatus.BAD_REQUEST.value()));
        }else if(!code.equals(o.toString())){
            throw new BaseException("请输入正确的验证码",new ResultResponse("请输入正确的验证码",HttpStatus.BAD_REQUEST.value()));
        }

        List<User> users = this.thisRepository.fetchByUserName(userDto.getUserName());
        //判断数据是否存在或者密码是否一致

        if(users.size()!=1 || !users.get(0).getPassword().equals(AESUtils.encrypt(userDto.getPassword()))){
            throw new BaseException("用户名或者密码错误",new ResultResponse("用户名或者密码错误",HttpStatus.BAD_REQUEST.value()));
        }

        if(isApplicant && !"APPLICANT".equals(users.get(0).getUserType().getSign())){
            throw new BaseException("该用户无法登录该平台",new ResultResponse("该用户无法登录该平台",HttpStatus.BAD_REQUEST.value()));
        }

        final String token = JwtUtils.create(users.get(0));
        // final User user = users.get(0);
        return new UserResponse(token, RSAUtils.encrypt(userDto.getUserName(), RSAUtils.PUBLIC_KEY),"用户登录成功");
    }

    @Override
    public UserResponse register(User user) {

        //验证密码强度
        if(!MathUtils.rexCheckUserName(user.getUserName())){
            throw  new  BaseException("用户名格式不对",new ResultResponse("用户名格式不对",HttpStatus.BAD_REQUEST.value()));
        }

        //验证密码强度
        if(!MathUtils.rexCheckPassword(user.getPassword())){
            throw  new  BaseException("密码格式不对",new ResultResponse("密码格式不对",HttpStatus.BAD_REQUEST.value()));
        }
        //MD5加密 后加密存储
        String decrypt = AESUtils.encrypt(DigestUtils.md5DigestAsHex(user.getPassword().getBytes()));
        user.setPassword(decrypt);
        user.setCurrentIp(CommonUtils.customIp(request));
        user.setUserType(UserType.APPLICANT);

        saveUser(user);

        final UserInfo userInfo = user.getUserInfo();

        if(userInfo!=null){
            userInfo.setUser(user);
            userInfoService.save(userInfo);
        }
        final String token = JwtUtils.create(user);
        System.out.println("register:"+token);
        return new UserResponse(token, RSAUtils.encrypt(user.getUserName(), RSAUtils.PUBLIC_KEY),"用户注册成功,请登录账号");
    }

    @Override
    public UserResponse alterPassword(UserPasswordDto userPasswordDto) {

        if(!userPasswordDto.getOldPassword().equals(userPasswordDto.getOldSecPassword())){
            throw new BaseException("输入旧密码不一致",new ResultResponse("输入旧密码不一致",HttpStatus.BAD_REQUEST.value()));
        }

        //验证密码强度
        if(!MathUtils.rexCheckPassword(userPasswordDto.getPassword())){
            throw  new  BaseException("密码格式不对",new ResultResponse("密码格式不对",HttpStatus.BAD_REQUEST.value()));
        }

        final List<User> users = this.thisRepository.fetchByUserName(userPasswordDto.getUserName());
        if(users.size()!=1){
            throw  new  BaseException("用户不存在或被删除",new ResultResponse("用户不存在或被删除",HttpStatus.BAD_REQUEST.value()));
        }

        User user = users.get(0);

        user.setPassword(AESUtils.encrypt(DigestUtils.md5DigestAsHex(userPasswordDto.getPassword().getBytes())));

        super.save(user);

        final String token = JwtUtils.create(user);
        System.out.println(token);
        return new UserResponse(token,RSAUtils.encrypt(user.getUserName(), RSAUtils.PUBLIC_KEY),"密码修改成功");
    }

    @Override
    //TODO
    public void logout() {
        JwtUtils.handleTokenExpire("");
    }

    @Override
    public User fetchOneByUserName(String userName) {
        return this.thisRepository.getUserByUserName(userName);
    }


    /**
     * 注册使用保存
     * @param user
     * @return
     */
    public User saveUser(User user) {
        //排查是否有相同的用户名
        final List<User> users = this.thisRepository.fetchByUserName(user.getUserName());

        if(user.getId()==null && users.size()>0){
            throw new BaseException("用户名不可用",new  ResultResponse("用户名不可用",HttpStatus.BAD_REQUEST.value()));
        }

        user.setCreateTime(LocalDateTime.now());
        user.setCreateUserId(-1L);
        return thisRepository.save(user);
    }

    public static void main(String[] args) {
        //MathUtils.md5("xayy@0021");
        //System.out.println(AESUtils.encrypt(MathUtils.md5("xayy@0021")));

        //System.out.println(AESUtils.encrypt(MathUtils.md5("9ea6de3ddaf40d8a4f4c9669a101b2b0")));
        // Vbdn8KEvlfF/yhr3Q7X5QsQ1EUoytngnU97kmb8UJDXN194MuuM5SpOCUlOyw+xV
        //System.out.println(AESUtils.encrypt(MathUtils.md5("xayy@2022")));

        //System.out.println(AESUtils.encrypt(MathUtils.md5("@kXkS%JH89")));
        //eHwoxsYL4i9Mqz40h2UNB4xyO8SrvpCHsHqvbBphi/jN194MuuM5SpOCUlOyw+xV
        System.out.println(DigestUtils.md5DigestAsHex("xayy@2023".getBytes()));
        System.out.println(MathUtils.md5("xayy@2023",64));
    }
}
